/** @file GOTHethernet.c This file contains the source code for the ETHERNET API of the GOTH
 * Kick-ass Etherboard.
 * 
 * The ETHERNET controller must be connected to SPI1
 * The ETHERNET controller is type ENC28J60
 *
 * Sets the SPI1 to a frequency of 16MHz
 *
 *
 * @author Geoffrey Ottoy - Tom Hamelinckx
 * @date 07/07/2011 (last modified)
 *
 * <pre>
 * MODIFICATION HISTORY:
 *
 * Who  Date     Changes
 * ---- -------- ---------------------------------------------------------
 * TH   06/07/11 Created this file.
 * TH	07/07/11 Testround 1
 * GO   07/07/11 Added eth interrupt (INT3)
 * TH	07/07/11 Corrections to INT-code and testrounds
 * </pre>
 * 
 */

#include "GOTHethernet.h"

/// stores the number of eth interrupts
volatile int eth_interrupt;

/** Initialize the ETH_INT pin (RP10 resp. RF4 on the kick-ass etherboard)
 *
 * @return	nothing
 */
void ETH_InitInterrupt(){
	eth_interrupt = 0;
	
	TRISFbits.TRISF4 = 1;		// set pin as input
	// pu & pd are currently disabled, but might be necessary
	CNPU2bits.CN17PUE = 0;		// pull-up disabled
	CNPD2bits.CN17PDE = 0;		// pull-down disabled
	RPINR1bits.INT3R = 10;		// map INT3 to RP10
	
	INTCON2bits.INT3EP = 0;		// interrupt on falling edge
	IPC13bits.INT3IP = 7;		// highest priority
	IFS3bits.INT3IF = 0;		// clear interrupt flag
	IEC3bits.INT3IE = 1;		// enable interrupt
}

/** Check if the ETH_INT has occured.
 *
 * @return	The number of interrupts (which is than set to 0). 
 */
int ETH_Interrupt(){
	int retval = eth_interrupt;
	eth_interrupt = 0;
	return retval;
}

/** The INT3 isr (which is associated with ETH_INT)
 *
 * @return	nothing
 */
void __attribute__((interrupt,no_auto_psv)) _INT3Interrupt(void){	
	// set status variable
	eth_interrupt++;

	/// @todo	you can add application specific code here
	
	IFS3bits.INT3IF = 0;    //Clear the INT3 interrupt flag or else
                            //the CPU will keep vectoring back to the ISR
}

/** Initialize SPI1 for operation.
 * 
 * @return      nothing
 *
 * @warning Attached ethernet controller has a maximum allowed clock of 20 MHz! Do not exceed!
 */
 void ETH_InitSpi1(void){
    SPI1STATbits.SPIEN = 0;                  // Disable SPI2 for peace of mind!
    AD1PCFGLbits.PCFG15 = 1;                //Tell analog it's a digital on /reset (ethernet)
    TRISBbits.TRISB15 = 0;                  // Set ETH-reset as output      
    LATBbits.LATB15 = 0;                    // set /reset low: reset
    
    //Set prescaling to 16 MHz clock
    SPI1CON1bits.PPRE = 3;                  //Primary prescaler: 32 MHz divby1 ==> 32MHz
    SPI1CON1bits.SPRE = 6;                  //Secondary Prescaler: 32 MHz divby2 ==> 16MHz
   
    SPI1CON1bits.CKE = 1;                   //Set clock edge (SDO on CLK active to CLK idle)
    //SPI1CON1bits.CKE = 0;                 //Set clock edge (SDO on CLK idle to CLK active)
    SPI1CON2bits.SPIBEN = 0;                //Disable Enhanced mode!
    
    CNPD5bits.CN71PDE = 0;                  //Disable pull-downs on SDI1
    CNPU5bits.CN71PUE = 0;                  //Disable pull-ups on SDI1
    RPINR20bits.SDI1R = 16;                 //Set SDI1 to RPI41
    RPOR15bits.RP30R = 7;                   //Set SDO1 to RP21
    RPOR8bits.RP17R = 8;                    //Set SCK1Out to RP26 (Out because it's master) 
    
    TRISFbits.TRISF8 = 0;                   // Set /CS as output
    LATFbits.LATF8 = 1;                     // Set /CS high (so ETH is NOT selected)
    
    
    LATBbits.LATB15 = 1;                    // set /reset high: stop reset
    
    SPI1STATbits.SPIROV = 0;                //Clear SPI Overflow!
    SPI1CON1bits.MSTEN = 1;                 //Enable SPI1 as master
    SPI1STATbits.SPIEN = 1;                 //Enable SPI1!
       

 
 }

/** Send data to the Ethernet Controller and read back its answer.
 * 
 * To keep clocking the device and just waiting for its response, keep sending 0
 *
 * @param       data    One byte of data to send out
 *
 * @return      The response from the readback
 */
unsigned char ETH_TransmitReceive(unsigned char data){
    while(SPI1STATbits.SPITBF == 1);
    SPI1BUF = data;
    while(!SPI1STATbits.SPIRBF);
    return SPI1BUF;
}

/** Perform a soft reset on the Ethernet controller
 * 
 * Datasheet claims this is EQUAL to hard reset
 *
 * @return	nothing
 */
void ETH_SoftReset(void){
	ETH_Select();
	ETH_TransmitReceive(ETH_SOFT_RESET);
	ETH_Deselect();
}

/** Select the bank where the given registername is in
 * 
 *
 * @param		registername	The register of which you want to select the bank
 *
 * @return      nothing
 */
void ETH_SetBank(unsigned char registername){
	ETH_Select();
	ETH_TransmitReceive((ETH_WRITE_CR | (ETH_ECON1 & 0x1F)));
	ETH_TransmitReceive((registername & 0xC0)>>6);
	ETH_Deselect();
}

///Work in progress: Trying something crazy! ;-)

/** Read a control register from the Ethernet controller
 * 
 * This command automaticly selects the correct bank!
 *
 * @param       registername    The register you want to read
 *
 * @return      The register value
 */
unsigned char ETH_ReadControl(unsigned char registername){
	unsigned char ret_val;
	ETH_SetBank(registername);
	ETH_Select();
	ETH_TransmitReceive((ETH_READ_CR | (registername & 0X1F)));
	ret_val = ETH_TransmitReceive(0);
	ETH_Deselect();
	return ret_val;
}

/** Write a control register 
 * 
 * This command automaticly selects the correct bank!
 *
 * @param       registername    The register to write to
 *				value			The value to be written
 *
 * @return      nothing
 */
void ETH_WriteControl(unsigned char registername, unsigned char value){
	ETH_SetBank(registername);
	ETH_Select();
	ETH_TransmitReceive((ETH_WRITE_CR | (registername & 0x1F)));
	ETH_TransmitReceive(value);
	ETH_Deselect();
}	

/** Write a PHY Register of the Ethernet controller
 * 
 * This command follows the entire protocol to write to the special PHY registers
 *
 * @param       registername	the PHY-registername
 *				value			Integer (16 bit!) to send
 *
 * @return      nothing
 */
void ETH_WritePHYReg(unsigned char registername, unsigned int value){
	unsigned char val_h, val_l;
	val_l = (unsigned char)(value & 0x00FF);
	val_h = ((unsigned char)((value & 0xFF00)>>8));
	
	ETH_SetBank(ETH_MIREGADR);
	ETH_WriteControl(ETH_MIREGADR, registername);
	ETH_WriteControl(ETH_MIWRL, val_l);
	ETH_WriteControl(ETH_MIWRH, val_h);
}

/** Read a PHY Register of the Ethernet Controller
 * 
 * This command follows the entire protocol to read from the special PHY registers
 * The function is blocking until the value is ready! (At least 10.24us!)
 *
 * @warning		EXPERIMENTAL
 *
 * @param       registername    the PHY-registername
 *
 * @return      the integer (16bit) that you requested!
 */
unsigned int ETH_ReadPHYReg(unsigned char registername){
//Good gravey! This will probably NOT work!!!
	unsigned char val_h, val_l;
	unsigned char test_val = 0x01;
	
	ETH_SetBank(ETH_MIREGADR);
	ETH_WriteControl(ETH_MIREGADR, registername);
	ETH_WriteControl(ETH_MICMD,0x01);
	ETH_SetBank(ETH_MISTAT);
	while(test_val == 1){
		test_val = (0X01 & ETH_ReadControl(ETH_MISTAT));
	}
	ETH_SetBank(ETH_MICMD);
	ETH_WriteControl(ETH_MICMD, 0x00);
	val_h = ETH_ReadControl(ETH_MIRDH);
	val_l = ETH_ReadControl(ETH_MIRDL);
	return ((val_h << 4) & val_l);
}

